import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { BASE_URL, request } from "../utils/request";
import { MemoItem } from "../../views/Functions/children/Memo/Memo";
import { TimerType } from "../../views/Functions/children/Timer/Timer";
import { SnippetType } from "../../views/Functions/children/Eval/Eval";
import { ClipboardType } from "../../views/Functions/children/Clipboard/Clipboard";
import {
  FetchKeywordType,
  KeywordType,
  KeywordWithCollectionType,
  PasswordPair,
} from "../domain/keyword";
import {
  MoveFileType,
  UploadFileType,
} from "../../views/Functions/children/Upload/Upload";
import { SongList, SongListVo } from "../domain/SongList";
import { Song } from "../domain/Song";
import { partialRandom, Random } from "../domain/Random";
import { User } from "../../components/LoginAndRegister/Login/Login";
import { FetchMemoType } from "../store/MemoSlice";
import { RegisterUserType } from "../../components/LoginAndRegister/Register/Register";
import { CollectionType } from "../domain/CollectionType";
import { fetchPasswordType, PasswordType } from "../../views/Password/Password";
import { PageInfoType } from "../store/uploadSlice";

export interface Result<T> {
  success: boolean;
  msg: string;
  code: number;
  data: T;
  token: string;
}

export interface PageResult<T> extends Result<T> {
  pageNo: number;
  pageSize: number;
  total: number;
}

declare global {
  interface Promise<T> {
    getData(): Promise<T extends Result<infer P> ? P : unknown>;
  }
}

Object.assign(Promise.prototype, {
  getData: function (this: typeof Promise.prototype) {
    return this.then(({ data }) => data);
  },
});

type RequestArgType = [string, data?: object, config?: AxiosRequestConfig];

// param?: AxiosRequestConfig["params"],

const initRequest = (path: string) => {
  const ret = async <T>(...args: RequestArgType) => {
    const [url, params] = args;
    return request<Result<T>>(path + url, params).then(({ data }) => data);
  };

  ret.raw = async <T>(...args: RequestArgType) => {
    const [url, config] = args;
    try {
      const { data } = await request<T>(path + url, config); //.then(({ data }) => data);
      return data;
    } catch (err) {
      console.log(err);
      throw err;
      // throw Error("network err");
    }
  };

  const module = async <T>(
    type: "post" | "delete" | "put",
    ...args: RequestArgType
  ) => {
    const [url, data, config] = args;
    return request[type]<T>(path + url, data, config).then(({ data }) => data);
  };

  ret.post = <T>(...args: RequestArgType) => module<T>("post", ...args);

  ret.put = <T>(...args: RequestArgType) => module<T>("put", ...args);

  ret.delete = <T>(...args: RequestArgType) => module<T>("delete", ...args);
  return ret;
};

const intercept = (
  composition: boolean = false,
  foo: ReturnType<typeof initRequest>
) => {
  const ref = foo;
  return <T>(...args: RequestArgType) => {
    type AssertAuthorization = { headers: { Authorization: string } };
    if (!args[1]) args[1] = {};
    if (!args[2]) args[2] = {};
    let headers =
      (args?.[composition ? 2 : 1] as AssertAuthorization)?.headers || {};
    headers = {
      ...headers,
      Authorization:
        localStorage.getItem("Authorization") || "no authorization",
    };
    (args[composition ? 2 : 1] as AssertAuthorization).headers = headers;
    return ref<T>(...args);
  };
};

// type indexable = { [keys: string]: any };
const interceptConstructor = (req: ReturnType<typeof initRequest>) => {
  const mock = intercept(false, req) as any;

  Object.assign(mock, req);

  mock.raw = intercept(false, mock.raw);
  mock.post = intercept(true, mock.post);
  mock.delete = intercept(false, mock.delete);
  mock.put = intercept(true, mock.put);
  // for (const key in req)
  //   (mock as indexable)[key] = intercept(true, (mock as indexable)[key]);

  return mock as unknown as typeof req;
};

class AccessApi {
  request = interceptConstructor(initRequest("/access"));

  public async online() {
    return this.request.post<Result<null>>("/test");
  }

  public async refreshIp() {
    return this.request<Result<null>>("/refreshIp");
  }

  public async validate() {
    return this.request.post("/validate");
  }
}

class KeywordApi {
  request = interceptConstructor(initRequest("/KeywordApi"));

  public async findByKey(search: string) {
    return this.request.raw<Result<KeywordType>>("/findByKey", {
      params: { search },
    });
  }

  public async updateRanks(keyword: KeywordType) {
    return this.request.raw<Result<KeywordType>>("/updateRanks", {
      params: keyword,
    });
  }

  public async fetchPlaceholder(id: KeywordType["id"]) {
    return this.request.raw<Result<KeywordWithCollectionType>>(
      "/findKeywordById",
      {
        params: { id },
      }
    );
  }

  public async fetchPlaceholders({
    search = "",
    pageNo = 0,
    pageSize = 50,
    filterList = [],
  }: Partial<{
    search: string;
    pageNo: number;
    pageSize: number;
    filterList: CollectionType[];
  }>) {
    // return this.request.post<FetchKeywordType>("/getKeywordList", {
    //   page: pageNo,
    //   size: pageSize,
    //   search,
    //   filterList,
    // });
    return this.request.post<FetchKeywordType>("/getKeywordList", filterList, {
      params: {
        page: pageNo,
        size: pageSize,
        search,
      },
    });
  }
  // keyName: string, url: string
  // {
  //         keyName,
  //         url,
  //       }
  public async addPlaceholder(data: Partial<KeywordType>) {
    return this.request.post<Result<KeywordType>>("/addKeyword", data);
  }

  public async removeKeywordById(id: KeywordType["id"]) {
    return this.request<KeywordType>("/removeKeywordById", {
      params: {
        id,
      },
    });
  }
  public async removeKeywordByKey(keyName: string) {
    return this.request<KeywordType>("/removeKeywordByKey", {
      params: {
        keyName,
      },
    });
  }

  // public async removePlaceholder(id: string) {
  //   return this.request("/removeKeyword", {
  //     params: {
  //       id,
  //     },
  //   });
  // }

  // id: string, keyName: string, url: string
  public async updatePlaceholder(data: KeywordType) {
    return this.request.post<KeywordType[]>("/updateKeyword", data);
  }
}

class MemoApi {
  request = initRequest("/MemoApi");

  public fetchMemoList({
    search = "",
    pageNo = 1,
    pageSize = 10,
  }: Partial<{
    search: string;
    pageNo: number;
    pageSize: number;
  }>) {
    return this.request.raw<FetchMemoType>("/getMemoList", {
      params: {
        search,
        page: pageNo,
        pageSize,
      },
    });
  }

  public getMemoById(id: MemoItem["id"]) {
    return this.request<MemoItem>("/getMemoById", {
      params: { id },
    }).getData();
  }

  public addMemo(memo: MemoItem) {
    return this.request.post<MemoItem[]>("/addMemo", memo);
  }

  public removeMemo(id: string) {
    return this.request<MemoItem[]>("/removeMemo", {
      params: {
        id,
      },
    }).getData();
  }

  public exchangeMemoSeq(id1: string, id2: string) {
    return this.request<MemoItem[]>("/exchangeMemoSeq", {
      params: {
        id1,
        id2,
      },
    }).getData();
  }
}

class ClipboardApi {
  private request = initRequest("/ClipboardApi");

  public getClipboard() {
    return this.request<ClipboardType>("/getClipboard", {
      method: "post",
    }).getData();
  }

  public toDocx(content: string) {
    return this.request<string>("/richtextToWord", {
      method: "post",
      data: {
        content,
      },
    }).getData();
  }

  public setClipboard(content: string) {
    return this.request("/setClipboard", {
      method: "post",
      // params: { content, id: 1 },
      data: {
        id: 1,
        content,
      },
    }).getData();
  }
}

class TimerApi {
  private request = initRequest("/TimerApi");

  public getTimerList() {
    return this.request<TimerType[]>("/getTimerList").getData();
  }

  public removeTimer(id: string) {
    return this.request("/removeTimer", { params: { id } }).getData();
  }

  public addTimer(name: string, memo: string) {
    return this.request("/addTimer", { params: { name, memo } }).getData();
  }

  public updateTimerHistory(id: string, time: string) {
    return this.request("/updateTimerHistory", { params: { id, time } });
  }

  public addTimerHistory(belongTo: string, time: string) {
    return this.request("/addTimerHistory", { params: { belongTo, time } });
  }

  public removeTimerHistory(id: string | number) {
    return this.request("/removeTimerHistory", { params: { id } });
  }

  public setPriority(id: string | number) {
    return this.request("/updateTimerPriority", { params: { id } });
  }
}

class SnippetApi {
  private request = initRequest("/SnippetApi");

  public getSnippetList() {
    return this.request<SnippetType[]>("/getSnippetList").getData();
  }

  public removeSnippet(id: number | string) {
    return this.request("/removeSnippet", { params: { id } });
  }

  public addSnippet(snippet: SnippetType) {
    delete (snippet as Partial<SnippetType>).id;
    return this.request("/addSnippet", { params: snippet });
  }

  public updateSnippet(snippet: SnippetType) {
    return this.request("/updateSnippet", { params: snippet });
  }
}

class BiliApi {
  private request = initRequest("/access");
  public downloadBili(url: string, type: string) {
    return this.request<{ name: string; location: string }>("/downloadBili", {
      params: { url, type },
    });
  }
}

class UploadApi {
  private request = interceptConstructor(initRequest("/UploadApi"));
  public getUploadList(
    path: string,
    pageNo: PageInfoType["pageNo"],
    pageSize: PageInfoType["pageSize"]
  ) {
    return this.request.raw<PageResult<UploadFileType[]>>(
      "/getUploadFileList",
      {
        params: { path, pageNo, pageSize },
      }
    );
  }
  public createFolder(path: string) {
    return this.request<null>("/createFolder", { params: { path } });
  }

  public delUploadFiles(fileName: string[]) {
    // return this.request<null>("/delUploadFiles", { data: { fileName } });
    return this.request.post<null>("/delUploadFiles", fileName);
  }
  public delUploadFile(fileName: string) {
    return this.request<null>("/delUploadFile", { params: { fileName } });
  }

  public download(fileName: string) {
    return BASE_URL + "UploadApi/download" + fileName;
  }

  public upload() {
    return BASE_URL + "UploadApi/upload";
  }

  public uploadFiles(fd: FormData) {
    return axios.post(this.upload(), fd, {
      headers: {
        Authorization: localStorage.getItem("Authorization"),
      },
    });
  }
  public rename(data: { from: string; to: string }) {
    return this.request.post<null>("/rename", data);
  }
  public pasteFiles(data: MoveFileType[]) {
    return this.request.post<null>("/move", data);
  }
  public clearUpload() {
    return this.request<null>("/clearUploadFolder");
  }
}

class SongApi {
  private request = initRequest("/SongApi");
  public song() {
    return BASE_URL + "upload/";
  }
  public getAllSongs(pageNo: number, pageSize: number = 10) {
    return this.request<Song[]>("/getAllSong", {
      params: {
        pageSize,
        pageNo,
      },
    }).getData();
  }
  public addSong(song: Song) {
    return this.request<Song>("/addSong", {
      params: song,
    });
  }
  public removeSong(id: number) {
    return this.request<Song>("/removeSong", {
      params: {
        id,
      },
    });
  }

  public removeSongs(ids: Song["id"][]) {
    return this.request<Song>("/removeSongs", {
      headers: {
        "Content-Type": "application/json",
      },
      method: "post",
      data: JSON.stringify(ids.map((val) => ({ id: val }))),
    });
  }
}

class SongListApi {
  private request = initRequest("/SongListApi");
  public createSongList(listName: string) {
    return this.request<null>("/createSongList", {
      params: { listName },
    });
  }
  public getAllList() {
    return this.request<SongListVo[]>("/getAllList").getData();
  }

  public getAllListData() {
    return this.request<SongListVo[]>("/getAllListData").getData();
  }

  public switchCurrentList(listName: string) {
    return this.request<null>("/switchCurrentList", {
      params: {
        listName,
      },
    });
  }
  public removeSongsFromList(listId: SongList["id"], songs: Song["id"][]) {
    return this.request<Song>("/removeSongsFromList", {
      headers: {
        "Content-Type": "application/json",
      },
      method: "post",
      params: { listId },
      data: JSON.stringify(songs.map((val) => ({ id: val }))),
    });
  }

  public removeList(id: number) {
    return this.request<null>("/removeList", {
      params: {
        id,
      },
    });
  }

  public addSongsToList(listId: number, songIds: string) {
    return this.request<null>("/addSongsToList", {
      params: {
        listId,
        songIds,
      },
    });
  }

  public changeListName(id: number, listName: string) {
    return this.request<null>("/changeListName", {
      params: {
        id,
        listName,
      },
    });
  }
}

class InjectApi {
  private request = initRequest("/RandomApi");
  public fetchScript(name: string) {
    return request(name);
    // return this.request<string>(name);
  }
}

class RandomApi {
  private request = initRequest("/RandomApi");

  public addRandom(ram: Random) {
    return this.request<number>("/addRandom", {
      params: ram,
    });
  }

  public updateRandom(ram: Random) {
    return this.request<number>("/updateRandom", {
      params: ram,
    });
  }
  public getRandom(ram: partialRandom) {
    return this.request<Random | null>("/getRandom", {
      params: ram,
    });
  }
  public delRandom(ram: Random) {
    return this.request<number>("/delRandom", {
      params: ram,
    });
  }
}

class PasswordApi {
  // private request = initRequest("/PasswordApi");
  private request = interceptConstructor(initRequest("/PasswordApi"));

  public async fetchPasswords(params: Partial<fetchPasswordType>) {
    return this.request.raw<PageResult<PasswordPair[]>>("/fetchPasswords", {
      params: {
        page: params.pageNo,
        size: params.pageSize,
        search: params.search,
        id: params.id,
      },
    });
  }

  // public async updatePasswords(
  //   id: KeywordType["id"],
  //   passwords: PasswordPair[]
  // ) {
  //   return this.request.post<PasswordPair[]>("/updatePasswords", passwords, {
  //     params: {
  //       id,
  //     },
  //   });
  // }

  public createPassword(pass: PasswordType) {
    return this.request.post<PasswordType>("/createPassword", pass);
  }
  public getAllPasswords(
    keyword: string = "",
    pageNo: number = 1,
    pageSize: number = 10
  ) {
    return this.request<PasswordType[]>("/getAllPasswords", {
      params: {
        keyword,
        pageSize,
        pageNo,
      },
    });
  }

  public deletePassword(id: number | string) {
    return this.request.delete<PasswordType>(`/${id}`);
  }
  // public updatePasswords(id: PasswordType["id"], pass: PasswordPair[]) {
  //   return this.request.put<PasswordType>(`/${id}`, pass);
  // }
  public insertPassword(id: PasswordType["id"], pass: PasswordPair) {
    return this.request.post<Result<null>>(`/insertPassword/${id}`, pass);
  }
  public updatePassword(id: PasswordType["id"], pass: PasswordPair) {
    return this.request.post<Result<null>>(`/updatePassword/${id}`, pass);
  }
  public delPassword(id: PasswordType["id"]) {
    return this.request.delete<Result<null>>(`/delPassword/${id}`);
  }
  public retrievePassword(id: number | string) {
    return this.request<PasswordPair>("/" + id, {
      headers: {
        Authorization: localStorage.getItem("Authorization"),
      },
    });
  }
}

class UserApi {
  private request = initRequest("/UserApi");
  public login(user: User) {
    return this.request.post<Result<unknown>>("/login", user);
  }
  public register(user: RegisterUserType, code: string) {
    return this.request.post<Result<unknown>>("/register?code=" + code, user);
  }
  public getCode(user: RegisterUserType) {
    return this.request("/getCode", {
      params: user,
    });
  }
  public forget(user: RegisterUserType, code: string) {
    return this.request.post<Result<unknown>>("/forget?code=" + code, user);
  }
}

class CollectionTypeApi {
  private request = interceptConstructor(initRequest("/CollectionTypeApi"));
  public getCollectionType({ pageNo = 1, pageSize = 10 }) {
    return this.request
      .post<Result<CollectionType[]>>("/getCollectionType", {
        pageNo,
        pageSize,
      })
      .getData();
  }
  public addCollectionType(data: CollectionType) {
    return this.request.post<Result<CollectionType>>(
      "/addCollectionType",
      data
    );
  }

  public updateCollectionType(data: CollectionType) {
    return this.request.post<Result<CollectionType>>(
      "/updateCollectionType",
      data
    );
  }
  public deleteCollectionType(data: CollectionType["id"]) {
    return this.request.post<Result<CollectionType>>("/deleteCollectionType", {
      id: data,
    });
  }
}

// class TestApi {
//   private request = interceptConstructor(initRequest("/access"));
//   public test() {
//     return this.request.post(
//       "/test",
//       {
//         para1: "url",
//       },
//       {
//         params: {
//           para2: "body",
//         },
//         headers: {
//           test: "header1",
//         },
//       }
//     );
//   }
// }

export const keywordApi = new KeywordApi();
export const memoApi = new MemoApi();
export const clipboardApi = new ClipboardApi();
export const timerApi = new TimerApi();
export const snippetApi = new SnippetApi();
export const biliApi = new BiliApi();
export const uploadApi = new UploadApi();
export const songApi = new SongApi();
export const songListApi = new SongListApi();
export const randomApi = new RandomApi();
export const injectApi = new InjectApi();
export const passwordApi = new PasswordApi();
export const userApi = new UserApi();
export const accessApi = new AccessApi();
export const collectionTypeApi = new CollectionTypeApi();
// export const testApi = new TestApi();
